﻿using OT.HikiSecureCenterApis.Res;
using OT.HikiSecureCenterApis.Service.OneCard;
using OT.HikiSecureCenterApis.Service.Resources;
using OT.HikiSecureCenterApis.Service.Video;

using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

namespace OT.HikiSecureCenterApis
{
    public class HikiSecureCenterApis
    {
        #region 属性
        private const string Accept = "Accept";
        private const string ContentType = "ContentType";
        private const string ContentMd5 = "ContentMd5";
        private const string Date = "Date";
        private const string XCaTimeStamp = "x-ca-timestamp";
        private const string XCaNonce = "x-ca-nonce";
        private const string XCaKey = "x-ca-key";
        private const string XCaSignature = "x-ca-signature";
        private const string XCaSignatureHeaders = "x-ca-signature-headers";
        private const string XCa = "x-ca-";
        /// <summary>
        /// 海康综合安防平台链接参数
        /// </summary>
        public SecureCenterOption option { get; private set; }
        /// <summary>
        /// 资源目录接口操作对象
        /// </summary>
        public ResourcesService resourcesService { get; private set; }
        /// <summary>
        /// 视频应用服务接口操作对象
        /// </summary>
        public VideoService videoService { get; private set; }
        /// <summary>
        /// 一卡通应用服务接口操作对象
        /// </summary>
        public OneCardService oneCardService { get; private set; }

        private HttpClient _httpClient { get; set; }
        #endregion

        /// <summary>
        /// 海康综合安防平台openapi操作构造函数
        /// </summary>
        /// <param name="_option">链接参数</param>
        public HikiSecureCenterApis(SecureCenterOption _option)
        {
            ResetInitOption(_option);
            // 设置并发数，如不设置默认为2
            ServicePointManager.DefaultConnectionLimit = 512;
            _httpClient = new HttpClient(new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
            });
            _httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
            InitService();
        }
        /// <summary>
        /// 初始化服务
        /// </summary>
        private void InitService() {
            resourcesService = new ResourcesService(this);
            videoService = new VideoService(this);
            oneCardService = new OneCardService(this);
        }
        /// <summary>
        /// 重置相关链接参数
        /// </summary>
        /// <param name="_option">链接参数</param>
        public void ResetInitOption(SecureCenterOption _option) => option = _option;
        public async Task<ResParameter<TResult>?> PostAsync<TResult>(string url, double ver, object body = null) 
        {
            //版本判断
            if (null == option || option.Ver < ver) { return new ResParameter<TResult> { code = "400", msg = "平台版本不支持该接口" }; }
            var bodyStr = null == body ? string.Empty : JsonSerializer.Serialize(body);
            var bodyJson = new StringContent(string.IsNullOrWhiteSpace(bodyStr) ? "" : bodyStr, Encoding.UTF8, "application/json");
            var header = InitHeader($"/artemis{url}");
            foreach (string headerKey in header.Keys)
            {
                if (headerKey.Contains(XCa))
                {
                    bodyJson.Headers.Add(headerKey, header[headerKey]);
                }
            }
            var response = await _httpClient.PostAsync($"{option.Url}/artemis{url}", bodyJson);
            string res = await response.Content.ReadAsStringAsync();
            return JsonSerializer.Deserialize<ResParameter<TResult>>(res);
        }

        #region 业务方法
        /// <summary cref="ResToken">
        /// 调用【获取API网关接口调用token】接口，获取返回参数access_token，access_token默认有效期为12小时
        /// 获取API网关接口调用token，无body入参。
        /// 此接口依旧使用AK\SK摘要认证方式。
        /// </summary>
        /// <remarks>综合安防管理平台iSecure Center V1.4及以上版本</remarks>
        /// <returns><see cref="ResToken"/></returns>
        /// <typeparam name="ResToken"></typeparam>
        public async Task<ResParameter<ResToken>?> TokenAsync() => await PostAsync<ResToken>("/api/v1/oauth/token", 1.4);
        #endregion

        #region 请求组装
        /// <summary>
        /// 组织海康综合安防平台
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private Dictionary<string, string> InitHeader(string url)
        {
            Dictionary<string, string> header = new Dictionary<string, string>
            {
                // Accept                
                { Accept, "application/json" },
                // ContentType  
                { ContentType, "application/json; charset=utf-8" }
            };
            // x-ca-timestamp
            string timestamp = ((DateTime.Now.Ticks - TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1, 0, 0, 0, 0), TimeZoneInfo.Local).Ticks) / 1000).ToString();
            header.Add(XCaTimeStamp, timestamp);
            // x-ca-nonce
            header.Add(XCaNonce, Guid.NewGuid().ToString());
            // x-ca-key
            header.Add(XCaKey, option?.AppKey);
            #region 加密处理
            StringBuilder sb = new StringBuilder();
            sb.Append("POST\n");
            if (header.ContainsKey(Accept)) { sb.Append($"{header[Accept]}\n"); }
            if (header.ContainsKey(ContentMd5)) { sb.Append($"{header[ContentMd5]}\n"); }
            if (header.ContainsKey(ContentType)) { sb.Append($"{header[ContentType]}\n"); }
            if (header.ContainsKey(Date)) { sb.AppendLine(header[Date]); }
            sb.Append($"{string.Join(string.Empty, header.Where(o => o.Key.Contains(XCa)).OrderBy(o => o.Key).Select(o => $"{o.Key}:{o.Value}\n"))}");
            sb.Append($"{url}");
            header.Add(XCaSignatureHeaders, string.Join(",", header.Where(o => o.Key.Contains(XCa)).OrderBy(o => o.Key).Select(o => o.Key)));
            header.Add(XCaSignature, ComputeForHMACSHA256(sb.ToString(), option?.Secret));
            #endregion
            return header;
        }
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="strToSign"></param>
        /// <param name="sk"></param>
        /// <returns></returns>
        private string ComputeForHMACSHA256(string strToSign, string sk)
        {
            var encoder = new System.Text.UTF8Encoding();
            byte[] secretBytes = encoder.GetBytes(sk);
            byte[] strBytes = encoder.GetBytes(strToSign);
            var opertor = new HMACSHA256(secretBytes);
            byte[] hashbytes = opertor.ComputeHash(strBytes);
            return Convert.ToBase64String(hashbytes);
        }
        #endregion
    }


}
